<html>
<head>
  <title>4. 原型（Prototype）模式</title>
  <basefont face="微软雅黑" size="2" />
  <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
  <meta name="exporter-version" content="YXBJ Windows/602628 (zh-CN, DDL); Windows/10.0.0 (Win64); EDAMVersion=V2;"/>
  <style>
    body, td {
      font-family: 微软雅黑;
      font-size: 10pt;
    }
  </style>
</head>
<body>
<a name="20697"/>
<h1>4. 原型（Prototype）模式</h1>
<div>
<table bgcolor="#D4DDE5" border="0">
<tr><td><b>创建时间：</b></td><td><i>2020/4/18 14:52</i></td></tr>
<tr><td><b>更新时间：</b></td><td><i>2021/3/11 1:00</i></td></tr>
<tr><td><b>作者：</b></td><td><i>Long Life Student</i></td></tr>
<tr><td><b>来源：</b></td><td><a href="https://github.com/PhycheLiu/DesignPattern_Study-Test"><i>https://github.com/PhycheLiu/DesignPattern_Study-Test</i></a></td></tr>
</table>
</div>
<br/>

<div>
<span><div><div><span style="font-size: 14pt; font-weight: bold;">一、基本描述</span></div><ul><li><div><span style="font-weight: bold;">用原型实例指定创建对象的种类，并且通过拷贝这些原型创建新的对象。</span></div></li><li><div>原型模式其实就是从一个对象再创建另外一个可定制的对象，而且不需知道任何创建的细节。</div></li><li><div>原型模式的实现与语言本身密切相关。</div></li><li><div>一般在初始化的信息不发生变化的情况下，原型模式是一个好的选择，这既可以隐藏对象创建的细节，又对性能有很大的提高</div></li><li><div><span style="font-weight: bold;">原型模式不用重新初始化对象，而是动态的获得对象运行时的状态。</span></div></li><li><div><span style="font-weight: bold;">原型模式在实现时有深克隆和浅克隆一说，需要根据情况进行选择。</span></div></li><li><div>原型模式很简单，所以应用的场景非常多。</div></li><li><div>原型模式的核心是一个clone方法，通过该方法进行对象拷贝，Java提供了一个Cloneable接口来表示这个对象是可拷贝的。但是这个clone方法实现的是浅拷贝，可以利用Object类的MemberwiseClone()或者序列化来实现深拷贝。</div></li><li><div>原型模式的工作原理：将一个原型对象传递给那个要发动创建的对象，要发动创建的对象通过请求原型对象拷贝自己来实现创建过程。</div></li><li><div>原型模式的核心在于如何实现克隆方法。</div></li><li><div>原型模式的特点在于通过“复制”一个已经存在的实例来返回新的实例，而不是新建实例，被复制的实例就是所谓的“原型”，这个“原型”是可定制的。</div></li><li><div>原型模式多用于创建复杂的或者耗时的实例，因为这种情况下，复制一个已经存在的实例使程序运行更高效，或者创建值相等，只是命名不一样的同类数据。</div></li><li><div>当创建给定类的实例的过程很昂贵或者很复杂时，就使用原型模式。</div></li><li><div>原型模式允许你通过复制现有的实例来创建新的实例（在Java中，这通常意味着使用 clone() 方法，或者反序列化）。<span style="color: unset; font-family: unset; font-size: unset;">这个模式的重点在于，客户的代码在不知道要实例化何种特定类的情况下，可以制造出新的实例。</span></div></li><li><div>在一个复杂的类层次中，当系统必须从其中的许多类型创建新对象时，可以考虑原型。</div></li></ul><div><font style="font-size: 14pt;"><span style="font-size: 14pt; font-weight: bold;">二、图例</span></font></div><div><img src="4. 原型（Prototype）模式_files/Image.png" type="image/png" data-filename="Image.png" width="437"/></div><ul><li><div>Prototype：声明一个克隆自身的接口；是声明克隆方法的接口，是所有具体原型类的公共父类，可以使抽象类也可以是接口，甚至可以是具体实现类；在这个类中， 最关键的是声明一个clone方法。</div></li><li><div>ConcretePrototype：实现一个克隆自身的操作；在实际应用时，这个类应先被实例化，然后才能实现克隆；实现在抽象原型类中声明的克隆方法，在克隆方法中返回自己的一个克隆对象。</div></li><li><div>Client：让一个原型克隆自身从而创建一个新的对象；由于客户类针对抽象原型类Prototype编程，因此用户可以根据需要选择具体原型类。</div></li></ul><div><font style="font-size: 14pt;"><span style="font-size: 14pt; font-weight: bold;">三、优缺点</span></font></div><div><span style="font-weight: bold;">优点：</span></div><ul><li><div>性能优良。比new一个对象性能好很多，特别是要在一个循环体内产生 大量的对象时。</div></li><li><div>逃避构造函数的约束。是优点，也是缺点，构造函数不会执行。</div></li><li><div>当创建新的对象实例较为复杂时，使用原型模式可以简化对象的创建过程。</div></li><li><div>扩展性较好。客户端可以针对抽象原型类进行编程，而将具体的原型类写在配置文件中，增加或减少具体原型类对原有系统没有影响。</div></li><li><div>原型模式提供了简化的创建结构。产品的复制是通过封装在原型类中的克隆方法实现的，无需专门的工厂类来创建产品。</div></li><li><div>向客户隐藏制造新实例的复杂性。</div></li><li><div>提供让客户能够产生未知类型对象的选项。</div></li><li><div>在某些环境下，复制对象比创建新对象更有效。</div></li></ul><div><span style="font-weight: bold;">缺点：</span></div><ul><li><div>逃避构造函数的约束。是优点也是缺点。</div></li><li><div>需要为每一个类配备一个克隆方法，当对已有的类进行改进时，要修改源代码。</div></li><li><div>对象的复制有时相当复杂。如：</div><div>在实现深克隆时要编写较复杂的代码，当对象之间存在多重嵌套引用时，为实现深克隆，每一层对象对应的类都必须支持深克隆，实现起来比较麻烦。</div></li></ul><div><span style="font-weight: bold; font-size: 14pt;">四、可能的改进</span></div><ol><li><div>使用克隆管理器对原型模式进行改进。（<a href="https://github.com/PhycheLiu/DesignPattern_Study-Test/tree/master/Cplus/Prototype/Prototype_1">C++版本</a>，<a href="https://github.com/PhycheLiu/DesignPattern_Study-Test/tree/master/Java/src/com/company/prototype/%E5%8E%9F%E5%9E%8B%E7%AE%A1%E7%90%86%E5%99%A8">Java版本</a>）</div></li><li><div><span style="font-size: 16px; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; color: rgb(36, 41, 46); font-family: -apple-system, BlinkMacSystemFont, &quot;Segoe UI&quot;, Helvetica, Arial, sans-serif, &quot;Apple Color Emoji&quot;, &quot;Segoe UI Emoji&quot;; font-variant-caps: normal; font-variant-ligatures: normal;"><a href="https://github.com/PhycheLiu/DesignPattern_Study-Test/tree/master/Java/src/com/company/prototype/%E7%9B%B8%E4%BC%BC%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%A4%8D%E5%88%B6" style="font-size: 16px; font-family: -apple-system, BlinkMacSystemFont, &quot;Segoe UI&quot;, Helvetica, Arial, sans-serif, &quot;Apple Color Emoji&quot;, &quot;Segoe UI Emoji&quot;; font-variant-caps: normal; font-variant-ligatures: normal;">相似对象的复制</a>。</span></div></li><li><div><a href="https://github.com/PhycheLiu/DesignPattern_Study-Test/tree/master/Java/src/com/company/prototype/sample02" style="color: rgb(36, 41, 46);">在Java中，使用序列化的方式实现深克隆。</a></div></li></ol><div><span style="font-weight: bold; font-size: 14pt;">五、个人理解</span></div><ul><li><div>原型模式可以参考“Ctrl+C”+“Ctrl+V”来理解。</div></li><li><div>原型模式很简单，其关键是一个clone方法，但是在具体实现这个clone方法的时候，有深拷贝与浅拷贝两种，很有讲究。</div></li><li><div>原型模式的实现与语言本身有很大的关系。</div></li><li><div>使用C++实现时，如果具体原型类中有成员变量，实现时必须在具体原型类中实现一个拷贝构造函数，但如果没有成员变量，拷贝构造函数可以不实现。</div></li></ul><div><span style="font-weight: bold; font-size: 14pt;">六、其他</span></div><div>      对Java中实现时的语句<span style="font-weight: bold;">super.clone()</span>的研究。</div><div>      在Java语言中实现clone方法时，一般都有一个核心的实现：super.clone()</div><div>      例如：</div><div>      <img src="4. 原型（Prototype）模式_files/Image [1].png" type="image/png" data-filename="Image.png" width="359"/></div><div>      经过以下代码的验证：</div><div>        this.getclass().getSuperclass().getName();</div><div>      可以发现，<span style="font-weight: bold;">这里的super其实就是java.lang.Object，也就是所有Java类的基类Object类。所以这里的super.clone()其实就是调用Object类中实现的clone()方法。</span></div><div>      由此，利用Java的基础设施实现克隆方法：</div><ol><li><div>利用Java的Object类，实现克隆方法，必须实现标识接口Cloneable。</div></li><li><div>Object类提供了一个Clone方法，可以将一个Java对象复制一份，Java中可以直接使用Object提供的Clone方法来实现对象的克隆。</div></li><li><div>直接采用Object类的clone方法：1）在具体原型类中覆盖基类的Clone方法；2）在派生类的Clone中，调用super.clone()（实际上就是调用Object的Clone方法）；3）派生类要实现Cloneable接口；4）此时，<span style="font-weight: bold;">Object类相当于抽象原型类，所有实现了Cloneable接口的类相当于具体原型类。</span></div></li></ol><div><span style="font-weight: bold; font-size: 14pt;">七、待研究</span></div><ul><li><div>都在说原型模式使用克隆，比使用构造函数new一个对象性能有很大的提高，到底是否是这样？不同的语言是否表现一样？</div></li></ul><div><br/></div></div><div><br/></div></span>
</div></body></html> 